package ysa;

import static java.lang.Math.signum;
import static java.lang.Math.toRadians;

import java.awt.geom.Point2D;

import robocode.AdvancedRobot;
import robocode.HitRobotEvent;
import robocode.ScannedRobotEvent;
import robocode.util.Utils;

/**
 * The implementation of the survivable robot for duels.
 */
public class Compose extends AdvancedRobot {
	
	private static final int RIGHT_ANGLE = 90;
	private static final int HIT_ROBOT_MOVING_DISTANCE = 100;
	private static final int FIRE_POWER_PARAM = 400;
	private static final int RADAR_TURN_PARAM = 400;
	private static final int DEFAULT_FIRE_POWER = 3;
	
	private double moveAmount; // how much to move
	private double firePower = 1;
	private double turn = 0;
	private double enemyX = Double.MIN_VALUE;
	private double enemyY = Double.MIN_VALUE;
	private double myPrevX = Double.MIN_VALUE;
	private double myPrevY = Double.MIN_VALUE;

	/**
	 * The main cycle of our robot. It's behavior shold be described here.
	 */
	@Override
	public void run() {
		turnRadarRightRadians(2 * Math.PI);
		moveAmount = Math.max(getBattleFieldWidth(), getBattleFieldHeight());
		turnLeft(getHeading() % RIGHT_ANGLE);
		setAhead(moveAmount);
		myPrevX = getX();
		myPrevY = getY();
		while (true) {
			setAhead(moveAmount);
			if (myPrevX == getX() && myPrevY == getY()) {
				turn += RIGHT_ANGLE;
				setTurnRight(turn);
				setTurnRadarRightRadians(2 * Math.PI);
				execute();
			} else {
				setTurnRadarRightRadians(getRadarTurn());
			}

			final double gunTurn = getGunTurn();
			setTurnGunRightRadians(gunTurn);
			setFire(firePower);
			myPrevX = getX();
			myPrevY = getY();
			execute();
		}
	}

	/**
	 * The event handler which works out when the enemy is in the radar scope.
	 */
	@Override
	public void onScannedRobot(ScannedRobotEvent event) {
		firePower = Math.min(FIRE_POWER_PARAM / event.getDistance(), DEFAULT_FIRE_POWER);
		enemyX = getX()
				+ Math.sin(getHeadingRadians() + event.getBearingRadians())
				* event.getDistance();
		enemyY = getY()
				+ Math.cos(getHeadingRadians() + event.getBearingRadians())
				* event.getDistance();
	}

	/**
	 * The event handler which works out when our robot hits the enemy.
	 */
	public void onHitRobot(HitRobotEvent e) {
		// if he's in front of us, set back up a bit
		if (e.getBearing() > -RIGHT_ANGLE && e.getBearing() < RIGHT_ANGLE) {
			back(HIT_ROBOT_MOVING_DISTANCE);
		} // else he's in back of us, so set ahead a bit
		else {
			ahead(HIT_ROBOT_MOVING_DISTANCE);
		}
	}

	private double getRadarTurn() {
		double alphaToEnemy = angleTo(getX(), getY(), enemyX, enemyY);
		double sign = (alphaToEnemy != getRadarHeadingRadians()) ? signum(Utils
				.normalRelativeAngle(alphaToEnemy - getRadarHeadingRadians()))
				: 1;
		return Utils.normalRelativeAngle(alphaToEnemy
				- getRadarHeadingRadians() + toRadians(RADAR_TURN_PARAM) * sign);
	}

	private static double angleTo(double baseX, double baseY, double x, double y) {
		double theta = Math.asin((y - baseY)
				/ Point2D.distance(x, y, baseX, baseY))
				- Math.PI / 2;
		if (x >= baseX && theta < 0) {
			theta = -theta;
		}
		return (theta %= Math.PI * 2) >= 0 ? theta : (theta + Math.PI * 2);
	}

	private double getGunTurn() {
		return Utils
				.normalRelativeAngle(angleTo(getX(), getY(), enemyX, enemyY)
						- getGunHeadingRadians());
	}
}